package ia.robot.graphics;

import ia.robot.app.Point;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.LinkedList;

import javax.swing.JPanel;

/**
 * Class that serves as a lightweight canvas component.
 * 
 * @author Jaime A. Corrales Gonzalez
 */
public class JCanvas extends JPanel {

	private static final long serialVersionUID = 1L;

	private int[] pixels;

	private Bitmap screen;
	private BufferedImage img;

	private LinkedList<Point> path;

	/**
	 * Constructor of JCanvas, given its width and height.
	 * 
	 * @param width
	 *            - Width of the canvas (in tiles).
	 * @param height
	 *            - Height of the canvas (in tiles).
	 */
	public JCanvas(int width, int height) {
		this.setDoubleBuffered(true);

		Dimension size = new Dimension(width * Textures.TILE_WIDTH, height
				* Textures.TILE_HEIGHT);

		this.setSize(size);
		this.setPreferredSize(size);
		this.setMinimumSize(size);
		this.setMaximumSize(size);

		screen = new Bitmap(width * Textures.TILE_WIDTH, height
				* Textures.TILE_HEIGHT);
		img = new BufferedImage(width * Textures.TILE_WIDTH, height
				* Textures.TILE_HEIGHT, BufferedImage.TYPE_INT_ARGB);
		pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();

		path = null;
	}

	/**
	 * Blits a bitmap to the canvas.
	 * 
	 * @param source
	 *            - Bitmap to blit.
	 */
	public void blitBitmap(Bitmap source) {
		screen.draw(source, 0, 0, 0, 0, source.getWidth(), source.getHeight());

		repaint();
	}

	public BufferedImage exportImage() {
		BufferedImage res = new BufferedImage(img.getWidth(), img.getHeight(),
				BufferedImage.TYPE_INT_ARGB);

		Graphics2D g = (Graphics2D) res.getGraphics();

		g.drawImage(img, 0, 0, getWidth(), getHeight(), null);

		g.setStroke(new BasicStroke(1));
		g.setColor(new Color(0, 0, 0, 50));

		int width = res.getWidth();
		int height = res.getHeight();

		for (int i = 0; i < width; i++)
			g.drawLine(i * Textures.TILE_WIDTH, 0, i * Textures.TILE_WIDTH,
					height);

		for (int i = 0; i < height; i++)
			g.drawLine(0, i * Textures.TILE_HEIGHT, width, i
					* Textures.TILE_HEIGHT);

		if (path != null) {
			g.setStroke(new BasicStroke(2));
			g.setColor(new Color(255, 0, 0, 55));

			int x1, x2, y1, y2;

			for (int i = 0; i < path.size() - 1; i++) {
				Point a = path.get(i);

				x1 = Textures.TILE_WIDTH * a.getX() + (Textures.TILE_WIDTH / 2);
				y1 = Textures.TILE_HEIGHT * a.getY()
						+ (Textures.TILE_HEIGHT / 2);
				x2 = Textures.TILE_WIDTH * path.get(i + 1).getX()
						+ (Textures.TILE_WIDTH / 2);
				y2 = Textures.TILE_HEIGHT * path.get(i + 1).getY()
						+ (Textures.TILE_HEIGHT / 2);
				
				g.drawLine(x1, y1, x2, y2);
			}
		}

		return res;
	}

	/**
	 * Paints the contents of the canvas on the screen.
	 */
	public void paintComponent(Graphics g) {
		// Invocamos al metodo de dibujo del padre
		super.paintComponent(g);

		// Convertimos el objeto Graphics en un Graphics2D para dibujar en Swing
		Graphics2D g2 = (Graphics2D) g;

		for (int i = 0; i < pixels.length; i++)
			pixels[i] = screen.getPixel(i);

		g2.fillRect(0, 0, getWidth(), getHeight());
		g2.drawImage(img, 0, 0, getWidth(), getHeight(), null);

		// Dibujamos el casillero
		g2.setStroke(new BasicStroke(1));
		g2.setColor(new Color(0, 0, 0, 50));
		int altura = (int) this.getSize().getWidth();
		int anchura = (int) this.getSize().getHeight();

		for (int i = 0; i < altura; i++)
			g2.drawLine(i * Textures.TILE_WIDTH, 0, i * Textures.TILE_WIDTH,
					anchura);

		for (int i = 0; i < anchura; i++)
			g2.drawLine(0, i * Textures.TILE_HEIGHT, altura, i
					* Textures.TILE_HEIGHT);

		// Dibujamos el camino
		if (path != null) {
			Point a;
			g2.setStroke(new BasicStroke(2));
			g2.setColor(new Color(255, 0, 0, 55));
			int x1, x2, y1, y2;
			for (int i = 0; i < path.size() - 1; i++) {
				a = path.get(i);
				x1 = Textures.TILE_WIDTH * a.getX() + (Textures.TILE_WIDTH / 2);
				y1 = Textures.TILE_HEIGHT * a.getY()
						+ (Textures.TILE_HEIGHT / 2);
				x2 = Textures.TILE_WIDTH * path.get(i + 1).getX()
						+ (Textures.TILE_WIDTH / 2);
				y2 = Textures.TILE_HEIGHT * path.get(i + 1).getY()
						+ (Textures.TILE_HEIGHT / 2);
				g2.drawLine(x1, y1, x2, y2);
			}
		}

		g2.dispose();
	}

	/**
	 * Resizes the canvas.
	 * 
	 * @param width
	 *            - New width of the canvas (in tiles).
	 * @param height
	 *            - New height of the canvas (in tiles).
	 */
	public void resizeCanvas(int width, int height) {
		Dimension size = new Dimension(width * Textures.TILE_WIDTH, height
				* Textures.TILE_HEIGHT);

		this.setSize(size);
		this.setPreferredSize(size);
		this.setMinimumSize(size);
		this.setMaximumSize(size);

		screen = new Bitmap(width * Textures.TILE_WIDTH, height
				* Textures.TILE_HEIGHT);
		img = new BufferedImage(width * Textures.TILE_WIDTH, height
				* Textures.TILE_HEIGHT, BufferedImage.TYPE_INT_ARGB);
		pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();

		path = null;
	}

	/**
	 * Sets the path followed by the robot, in order to draw the solution.
	 * 
	 * @param path
	 *            - Path followed by the robot.
	 */
	public void setPath(LinkedList<Point> path) {
		this.path = path;
	}
}
